package com.github.sbb.sherry.primitives.fetcher.mysql;

import com.github.sbb.sherry.common.base.Preconditions;
import com.github.sbb.sherry.common.base.ValidateResult;
import com.github.sbb.sherry.primitives.api.Fact;
import com.github.sbb.sherry.primitives.api.Facts;
import com.github.sbb.sherry.primitives.api.Fetcher;
import com.github.sbb.sherry.primitives.config.fetcher.MySQLFetcherConfig;
import com.github.sbb.sherry.primitives.core.MapFact;
import com.github.sbb.sherry.primitives.exception.FetcherException;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class MySQLFetcher implements Fetcher {

  private static final Pattern PARAM_PLACEHOLDER_CONDITION = Pattern.compile("#\\{[A-Za-z0-9_-]+}");

  private final String name;
  private final MySQLFetcherConfig config;
  private final MySQLDataSource dataSource;

  public MySQLFetcher(String name, MySQLFetcherConfig config, MySQLDataSource dataSource) {

    Preconditions.checkNotNull(name, "name can`t be null");
    Preconditions.checkNotNull(config, "MySQLFetcherConfig can`t be null");
    Preconditions.checkNotNull(dataSource, "MySQLDataSource can`t be null");

    ValidateResult validate = config.validate();
    Preconditions.checkArgument(validate.isPass(), validate.getMessage());

    this.name = name;
    this.config = config;
    this.dataSource = dataSource;
  }

  @Override
  public String name() {
    return name;
  }

  @Override
  public byte getDefaultFetchMethod() {
    return config.getDefaultFetchMethod();
  }

  @Override
  public Fact emit(Map<String, Object> params) throws FetcherException {
    try {
      Map<String, Object> objectMap = dataSource
          .selectOne(config.getTable(), config.getColumnItems(), buildCondition(params));
      return new MapFact(objectMap);
    } catch (Exception e) {
      throw new FetcherException(e);
    }
  }

  @Override
  public Facts batchEmit(Map<String, Object> params) throws FetcherException {
    try {
      Facts facts = new Facts();
      List<Map<String, Object>> mapList = dataSource
          .selectMany(config.getTable(), config.getColumnItems(), buildCondition(params), config.getLimit());
      for (Map<String, Object> objectMap : mapList) {
        facts.add(new MapFact(objectMap));
      }
      return facts;
    } catch (Exception e) {
      throw new FetcherException(e);
    }
  }

  private String buildCondition(Map<String, Object> params) {
    String condition = config.getCondition();
    for (Map.Entry<String, Object> param : params.entrySet()) {
      String key = "#{" + param.getKey() + "}";
      Object value = param.getValue();

      String replaceValue;
      if (value == null) {
        replaceValue = "''";
      } else if (value instanceof Number) {
        replaceValue = "" + value;
      } else {
        replaceValue = "'" + value + "'";
      }
      condition = condition.replace(key, replaceValue);
      if (!condition.contains("#")) {
        break;
      }
    }

    if (PARAM_PLACEHOLDER_CONDITION.matcher(condition).find()) {
      throw new RuntimeException();
    }

    return condition;
  }
}
